package com.dhms.uikit

import android.app.Dialog
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.NumberPicker
import androidx.core.view.children
import androidx.core.view.forEach
import com.dhms.uikit.databinding.FragmentTimePickerBottomSheetBinding
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import java.util.*

/**
 * @sample
 * val calendar = Calendar.getInstance()
 * calendar.set(Calendar.YEAR, 2004)
 * TimePickerBottomSheet().setDefaultTime(calendar).setOnTimeSelectedListener { time ->
 *     Log.d("1123", time.toString())
 * }.show(requireActivity().supportFragmentManager, "1123")
 */
class TimePickerBottomSheet : BottomSheetDialogFragment() {

    private lateinit var binding: FragmentTimePickerBottomSheetBinding

    private lateinit var bottomSheetDialog: BottomSheetDialog

    private var defaultTime: Calendar? = null
    private lateinit var onTimeSelectedListener: (date: Calendar) -> Unit

    private var isLeapYear = false

    private var isJustShowDatePicker = false
    private var isShowSecondPicker = false

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        bottomSheetDialog =
            BottomSheetDialog(requireContext(), R.style.Theme_UiKit_BottomSheetDialog)

        return bottomSheetDialog
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentTimePickerBottomSheetBinding.inflate(inflater, container, false)
        binding.isJustShowDatePicker = isJustShowDatePicker
        binding.isShowSecondPicker = isShowSecondPicker

        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        setTimePickerConfig()

        setTimePickerOnValueChangedListener()

        setTimePickerCustomConfig()

        initMaxDayOfMonth()
    }

    private fun setTimePickerConfig() {
        binding.timePickerNegativeBtn.setOnClickListener {
            bottomSheetDialog.dismiss()
        }

        binding.yearPicker.setFormatter {
            "$it 年"
        }
        binding.monthPicker.setFormatter {
            "$it 月"
        }
        binding.dayPicker.setFormatter {
            "$it 日"
        }
        binding.hourPicker.setFormatter {
            "$it 时"
        }
        binding.minutePicker.setFormatter {
            "$it 分"
        }
        binding.secondPicker.setFormatter {
            "$it 秒"
        }

        // Fix the problem that pickers' formatters are not work when the views are created first.
        binding.timePickersWrapper.forEach { pickerView ->
            (pickerView as NumberPicker).children.iterator().forEach {
                if (it is EditText) it.filters = arrayOfNulls(0)
            }
        }

        binding.yearPicker.minValue = 1970
        binding.yearPicker.maxValue = 2100

        binding.monthPicker.minValue = 1
        binding.monthPicker.maxValue = 12

        binding.dayPicker.minValue = 1
        binding.dayPicker.maxValue = 31

        binding.hourPicker.minValue = 0
        binding.hourPicker.maxValue = 23

        binding.minutePicker.minValue = 0
        binding.minutePicker.maxValue = 59

        binding.secondPicker.minValue = 0
        binding.secondPicker.maxValue = 59
    }

    private fun setTimePickerOnValueChangedListener() {
        binding.yearPicker.setOnValueChangedListener { _, _, newVal ->
            isLeapYear = isLeapYear(newVal)
            if (binding.monthPicker.value == 2) {
                if (isLeapYear) {
                    binding.dayPicker.maxValue = 29
                } else {
                    binding.dayPicker.maxValue = 28
                }
            }
        }

        binding.monthPicker.setOnValueChangedListener { _, _, newVal ->
            setDayOfMonthMaxValue(newVal, isLeapYear)
        }
    }

    private fun setTimePickerCustomConfig() {
        val calendar = Calendar.getInstance()

        if (defaultTime != null) {
            binding.yearPicker.value = defaultTime!!.get(Calendar.YEAR)
            binding.monthPicker.value = defaultTime!!.get(Calendar.MONTH) + 1
            binding.dayPicker.value = defaultTime!!.get(Calendar.DATE)
            binding.hourPicker.value = defaultTime!!.get(Calendar.HOUR_OF_DAY)
            binding.minutePicker.value = defaultTime!!.get(Calendar.MINUTE)
            binding.secondPicker.value = defaultTime!!.get(Calendar.SECOND)
        } else {
            binding.yearPicker.value = calendar.get(Calendar.YEAR)
            binding.monthPicker.value = calendar.get(Calendar.MONTH) + 1
            binding.dayPicker.value = calendar.get(Calendar.DATE)
            binding.hourPicker.value = calendar.get(Calendar.HOUR_OF_DAY)
            binding.minutePicker.value = calendar.get(Calendar.MINUTE)
            binding.secondPicker.value = calendar.get(Calendar.SECOND)
        }

        // 0 is January
        binding.timePickerPositiveBtn.setOnClickListener {
            if (this::onTimeSelectedListener.isInitialized) {
                calendar.set(
                    binding.yearPicker.value,
                    binding.monthPicker.value - 1,
                    binding.dayPicker.value,
                    binding.hourPicker.value,
                    binding.minutePicker.value,
                    binding.secondPicker.value
                )
                onTimeSelectedListener(calendar)

                Log.d(TAG, "The time $calendar is selected.")
            }
            bottomSheetDialog.dismiss()
        }
    }

    private fun initMaxDayOfMonth() {
        isLeapYear = isLeapYear(binding.yearPicker.value)
        setDayOfMonthMaxValue(binding.monthPicker.value, isLeapYear)
    }

    private fun setDayOfMonthMaxValue(month: Int, isLeapYear: Boolean) {
        when (month) {
            in BIG_MONTH_LIST -> binding.dayPicker.maxValue = 31
            2 -> {
                if (isLeapYear) {
                    binding.dayPicker.maxValue = 29
                } else {
                    binding.dayPicker.maxValue = 28
                }
            }
            else -> {
                binding.dayPicker.maxValue = 30
            }
        }
    }

    /**
     * To set whether only display the date picker.
     */
    fun setIsJustShowDatePicker(isJustShowDatePicker: Boolean): TimePickerBottomSheet {
        this.isJustShowDatePicker = isJustShowDatePicker

        return this
    }

    /**
     * To set whether display the second picker.
     */
    fun setIsShowSecondPicker(isShowSecondPicker: Boolean): TimePickerBottomSheet {
        this.isShowSecondPicker = isShowSecondPicker

        return this
    }

    /**
     * To set the default time of the time picker.
     */
    fun setDefaultTime(calendar: Calendar?): TimePickerBottomSheet {
        defaultTime = calendar
        Log.d(TAG, "The time $calendar is set.")

        return this
    }

    /**
     * To set the callback on the positive button clicked.
     * @Notice
     * 0 is January.
     */
    fun setOnTimeSelectedListener(onTimeSelectedListener: (date: Calendar) -> Unit): TimePickerBottomSheet {
        this.onTimeSelectedListener = onTimeSelectedListener

        return this
    }

    companion object {
        private const val TAG = "TimePickerBottomSheetFragment"

        private val BIG_MONTH_LIST = listOf(1, 3, 5, 7, 8, 10, 12)
    }
}